package exchange

import (
	"errors"
	"gitee.com/quant1x/gox/logger"
	gts "gitee.com/quant1x/gox/timestamp"
	"gitee.com/quant1x/pkg/yaml"
	"sort"
	"strconv"
	"strings"
)

var (
	TradeStageRangeConfig map[TradeStage][]int
	DataStageRangeConfig  map[DataStage][]int

	tradeStages []MilliRange
	dataStages  []MilliRange
)

type TimeStateConfig struct {
	TradeStages []*TimeRange `yaml:"trade_stages"`
	DataStages  []*TimeRange `yaml:"data_stages"`
}

// TimeRange 时间段配置文件 TradeStage 和 DataStage 二选一
type TimeRange struct {
	TradeStage TradeStage `name:"交易时段" yaml:"trade_stage"`
	DataStage  TradeStage `name:"数据时段" yaml:"data_stage"`
	Internal   string     `name:"时间区间" yaml:"interval"`
}

type MilliRange struct {
	Stage int
	Begin int64
	End   int64
}

func init() {
	TradeStageRangeConfig = make(map[TradeStage][]int)
	DataStageRangeConfig = make(map[DataStage][]int)

	TradeStageRangeConfig[TradeStage_Nil] = []int{0, 0, 0, 9, 0, 0}
	TradeStageRangeConfig[TradeStage_PreMarket] = []int{9, 0, 0, 9, 15, 0}
	TradeStageRangeConfig[TradeStage_Bidding_AM_Step1] = []int{9, 15, 0, 9, 20, 0}
	TradeStageRangeConfig[TradeStage_Bidding_AM_Step2] = []int{9, 20, 0, 9, 25, 0}
	TradeStageRangeConfig[TradeStage_Bidding_AM_Step3] = []int{9, 25, 0, 9, 30, 0}
	TradeStageRangeConfig[TradeStage_Open_AM] = []int{9, 30, 0, 11, 30, 0}
	TradeStageRangeConfig[TradeStage_Nooning] = []int{11, 30, 0, 13, 0, 0}
	TradeStageRangeConfig[TradeStage_Open_PM] = []int{13, 0, 0, 14, 57, 0}
	TradeStageRangeConfig[TradeStage_Bidding_PM] = []int{14, 57, 0, 15, 0, 0}
	TradeStageRangeConfig[TradeStage_Close] = []int{15, 0, 0, 24, 0, 0}

	DataStageRangeConfig[DataStage_Nil] = []int{0, 0, 0, 9, 0, 0}
	DataStageRangeConfig[DataStage_Init] = []int{9, 0, 0, 9, 15, 0}
	DataStageRangeConfig[DataStage_Bidding_AM] = []int{9, 15, 0, 9, 26, 0}
	DataStageRangeConfig[DataStage_Trading_AM] = []int{9, 30, 0, 11, 30, 0}
	DataStageRangeConfig[DataStage_Trading_PM] = []int{13, 0, 0, 14, 57, 30}
	DataStageRangeConfig[DataStage_Bidding_PM] = []int{14, 57, 0, 15, 00, 30}
	DataStageRangeConfig[DataStage_Close] = []int{15, 0, 0, 24, 0, 0}
}

func InitStageConfig(bytes []byte) error {
	timeStageCfg := &TimeStateConfig{}
	err := yaml.Unmarshal(bytes, &timeStageCfg)
	if err != nil {
		logger.Errorf("初始化交易时段配置失败,使用默认配置")
	}

	// HH:MM:SS 时间格式转换成毫秒数
	for _, stage := range timeStageCfg.TradeStages {
		if len(TradeStageRangeConfig[stage.TradeStage]) == 0 {
			return errors.New("配置文件格式错误, 交易阶段类型异常")
		}
		if arr, err := parseTimeString(stage.Internal); err != nil {
			return err
		} else {
			TradeStageRangeConfig[stage.TradeStage] = arr
		}
	}

	for _, stage := range timeStageCfg.DataStages {
		if len(DataStageRangeConfig[stage.DataStage]) == 0 {
			return errors.New("配置文件格式错误, 交易阶段类型异常")
		}
		if arr, err := parseTimeString(stage.Internal); err != nil {
			return err
		} else {
			DataStageRangeConfig[stage.DataStage] = arr
		}
	}

	for k, v := range TradeStageRangeConfig {
		timeRange := MilliRange{
			Stage: k,
			Begin: int64(gts.MillisecondsPerHour*v[0] + gts.MillisecondsPerMinute*v[1] + gts.MillisecondsPerSecond*v[2]),
			End:   int64(gts.MillisecondsPerHour*v[3] + gts.MillisecondsPerMinute*v[4] + gts.MillisecondsPerSecond*v[5]),
		}
		tradeStages = append(tradeStages, timeRange)
	}

	for k, v := range DataStageRangeConfig {
		timeRange := MilliRange{
			Stage: k,
			Begin: int64(gts.MillisecondsPerHour*v[0] + gts.MillisecondsPerMinute*v[1] + gts.MillisecondsPerSecond*v[2]),
			End:   int64(gts.MillisecondsPerHour*v[3] + gts.MillisecondsPerMinute*v[4] + gts.MillisecondsPerSecond*v[5]),
		}
		dataStages = append(dataStages, timeRange)
	}
	sort.Slice(tradeStages, func(i, j int) bool {
		return tradeStages[i].Begin < tradeStages[j].Begin
	})
	sort.Slice(dataStages, func(i, j int) bool {
		return dataStages[i].Begin < dataStages[j].Begin
	})
	return nil
}

func parseTimeString(s string) ([]int, error) {
	text := strings.TrimSpace(s)
	arr := stringRangeRegexp.Split(text, -1)
	if len(arr) != 2 {
		return nil, ErrTimeFormat
	}
	beginStr := strings.Split(strings.TrimSpace(arr[0]), ":")
	endStr := strings.Split(strings.TrimSpace(arr[1]), ":")
	b1, err := strconv.Atoi(beginStr[0])
	if err != nil {
		return nil, ErrTimeFormat
	}
	b2, err := strconv.Atoi(beginStr[1])
	if err != nil {
		return nil, ErrTimeFormat
	}
	b3, err := strconv.Atoi(beginStr[2])
	if err != nil {
		return nil, ErrTimeFormat
	}
	e1, err := strconv.Atoi(endStr[0])
	if err != nil {
		return nil, ErrTimeFormat
	}
	e2, err := strconv.Atoi(endStr[1])
	if err != nil {
		return nil, ErrTimeFormat
	}
	e3, err := strconv.Atoi(endStr[2])
	if err != nil {
		return nil, ErrTimeFormat
	}
	return []int{b1, b2, b3, e1, e2, e3}, nil
}

// 交易时段
type TradeStage = int

const (
	TradeStage_Nil              TradeStage = iota //
	TradeStage_PreMarket                          // 盘前
	TradeStage_Bidding_AM_Step1                   // 上午集合竞价9:15-9:20
	TradeStage_Bidding_AM_Step2                   // 上午集合竞价9:20-9:25
	TradeStage_Bidding_AM_Step3                   // 上午集合竞价9:25-9:30
	TradeStage_Open_AM                            // 上午开盘
	TradeStage_Nooning                            // 中午休市
	TradeStage_Open_PM                            // 下午开盘
	TradeStage_Bidding_PM                         // 下午集合竞价
	TradeStage_Close                              // 下班
)

// 数据阶段
type DataStage = int

const (
	DataStage_Nil        DataStage = iota //
	DataStage_Init                        // 初始化
	DataStage_Bidding_AM                  // 上午集合竞价
	DataStage_Trading_AM                  // 连续交易时间段
	DataStage_Trading_PM                  // 连续交易时间段
	DataStage_Bidding_PM                  // 下午集合竞价
	DataStage_Parse                       // 暂停
	DataStage_Close                       // 下班
)

// GetCurrentTradeStage 默认获取此时的交易时间段
// 回测时传指定毫秒值，判断那一刻处在哪一个交易时间段
func GetCurrentTradeStage(milliseconds ...int64) (tradeStage TradeStage, dataStage DataStage) {
	ts := gts.Now()
	if len(milliseconds) > 0 {
		ts = milliseconds[0]
	}

	// 假期判断
	if IsHolidayMs(ts) {
		return TradeStage_Close, DataStage_Close
	}

	gapMilliseconds := gts.SinceZero(ts)
	for _, stage := range tradeStages {
		if stage.Begin <= gapMilliseconds && stage.End > gapMilliseconds {
			tradeStage = stage.Stage
			break
		}
	}
	isParse := true
	for _, stage := range dataStages {
		if stage.Begin <= gapMilliseconds && stage.End > gapMilliseconds {
			dataStage = stage.Stage
			isParse = false
			break
		}
	}
	if isParse {
		dataStage = DataStage_Parse
	}
	return tradeStage, dataStage
}
